﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGinnBPM.Lib.Data;
using System.Runtime.Serialization;

namespace NGinnBPM.Lib.Schema
{
    /// <summary>
    /// Petri net element definition
    /// </summary>
    [Serializable]
    [DataContract]
    public abstract class NetNode : IHaveExtensionProperties
    {
        /// <summary>
        /// Net node identifier
        /// </summary>
        [DataMember]
        public string Id { get; set; }
        

        /// <summary>
        /// Label - node name shown to users
        /// </summary>
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string Label { get; set; }
        
        /// <summary>
        /// Net node description text. 
        /// </summary>
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string Description { get; set; }
        

        public virtual bool Validate(IList<ValidationMessage> msgs)
        {
            int cnt = msgs.Count;
            if (FlowsIn.Count == 0 && FlowsOut.Count == 0 && ParentTask != null)
                msgs.Add(new ValidationMessage(false, "Node without any connections", this));
            foreach (Flow fl in FlowsOut)
            {
                fl.Validate(msgs);
            }
            for (int i = cnt; i < msgs.Count; i++)
                if (msgs[i].IsError) return false;
            return true;
        }

        [IgnoreDataMember]
        public CompositeTask ParentTask { get; set; }


        [IgnoreDataMember]
        public virtual ProcessDefinition ParentProcessDefinition
        {
            get
            {
                return ParentTask.ParentProcessDefinition;
            }
        }
        
        

        /// <summary>
        /// List of flows going out from the node
        /// </summary>
        [IgnoreDataMember]
        public virtual ICollection<Flow> FlowsOut
        {
            get
            {
                if (ParentTask == null)
                    throw new Exception("Parent task is not defined");
                return ParentTask.GetChildNodeFlowsOut(this.Id);
            }
        }

        /// <summary>
        /// List of flows ordered by evaluation order
        /// </summary>
        [IgnoreDataMember]
        public IList<Flow> FlowsOutOrdered
        {
            get
            {
                List<Flow> nl = new List<Flow>(FlowsOut);
                nl.Sort(new Comparison<Flow>(CompareFlowOrder));
                return nl;
            }
        }

        /// <summary>
        /// Default flow out chosen
        /// </summary>
        [IgnoreDataMember]
        public Flow DefaultFlowOut
        {
            get
            {
                IList<Flow> lst = FlowsOutOrdered;
                foreach (Flow fl in lst)
                    if (fl.SourcePort == TaskOutPortType.Default && (fl.InputCondition == null || fl.InputCondition.Length == 0 || fl.EvalOrder == 0))
                        return fl;
                return lst[lst.Count - 1];
            }
        }

        protected static int CompareFlowOrder(Flow f1, Flow f2)
        {
            if (f1.EvalOrder < 0) return 1;
            if (f2.EvalOrder < 0) return -1;
            if (f1.EvalOrder < f2.EvalOrder)
                return -1;
            else if (f1.EvalOrder > f2.EvalOrder)
                return 1;
            else
                return 0;
        }

       

        /// <summary>
        /// List of nodes (places or tasks) following current node in the process definition
        /// </summary>
        [IgnoreDataMember]
        public IList<NetNode> NodesOut
        {
            get
            {
                if (ParentTask == null)
                    throw new Exception("Parent task is not defined");
                return ParentTask.GetChildNodeNodesOut(this.Id);
            }
        }

        /// <summary>
        /// List of flows leading to current node
        /// </summary>
        [IgnoreDataMember]
        public virtual ICollection<Flow> FlowsIn
        {
            get
            {
                if (ParentTask == null)
                    throw new Exception("Parent task is not defined");
                return ParentTask.GetChildNodeFlowsIn(this.Id);
            }
            
        }

        /// <summary>
        /// List of nodes preceding current node.
        /// </summary>
        [IgnoreDataMember]
        public IList<NetNode> NodesIn
        {
            get
            {
                if (ParentTask == null)
                    throw new Exception("Parent task is not defined");
                return ParentTask.GetChildNodeNodesIn(this.Id);
            }
        }

        

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public Dictionary<string, string> ExtensionProperties { get; set; }

        #region IHaveExtensionProperties Members

        public IEnumerable<string> GetExtensionProperties(string xmlns)
        {
            return ExtensionPropertyHelper.GetExtensionProperties(ExtensionProperties, xmlns);
        }

        public string GetExtensionProperty(string xmlns, string name)
        {
            return ExtensionPropertyHelper.GetExtensionProperty(ExtensionProperties, xmlns, name);
        }

        public void SetExtensionProperty(string xmlns, string name, string value)
        {
            if (ExtensionProperties == null) ExtensionProperties = new Dictionary<string, string>();
            ExtensionPropertyHelper.SetExtensionProperty(ExtensionProperties, xmlns, name, value);
        }

        public string GetExtensionProperty(string fullName)
        {
            return ExtensionPropertyHelper.GetExtensionProperty(ExtensionProperties, fullName);
        }

        #endregion

        /// <summary>
        /// This method will be called when a node
        /// is added to a parent composite task.
        /// </summary>
        /// <param name="parentTask"></param>
        public virtual void InitSchema(CompositeTask parentTask, ProcessDefinition parentProcess)
        {
            this.ParentTask = parentTask;
        }


    }
}
